package main

import  (
	flag "github.com/spf13/pflag"
    "fmt"
    "os"
    "io"
    "bufio"
    "os/exec"
)
const INT_MAX = int((1 << 32))

type selpg_args struct {
	start_page int
	end_page int
	page_len int
	page_type bool
	in_filename string
	print_dest string
}

var progname string

func main() {
    var sa selpg_args
    get_args(sa)
    check_args(sa)
    process_input(sa)

}

func get_args(sa selpg_args) {
	progname = os.Args[0]
	flag.IntVarP(&sa.start_page, "start_page", "s", -1, "start page")
    flag.IntVarP(&sa.end_page, "end_page", "e", -1, "end page")
    flag.IntVarP(&sa.page_len, "page_len", "l", 15, "page len")
    flag.BoolVarP(&sa.page_type, "page_type", "f", false, "page_type")
    flag.StringVarP(&sa.print_dest, "print_dest", "d", "", "print destination")

    flag.Parse()
    if flag.NArg() > 0 {
        sa.in_filename = flag.Arg(0)
    }
}

func usage() {
    fmt.Fprintf(os.Stderr, "\nUSAGE: %s -sstart_page -eend_page [ -f | -llines_per_page ] [ -ddest ] [ in_filename ]\n", progname)
}

func check_args(sa selpg_args) {
    if len(os.Args) < 3 {
        fmt.Fprintf(os.Stderr, "%s: not enough arguments\n", progname)
        usage()
        os.Exit(1)
    }
    if sa.start_page < 1 || sa.start_page > INT_MAX {
        fmt.Fprintf(os.Stderr, "%s: invalid start page %d\n", progname, sa.start_page)
        usage()
        os.Exit(2)
    }
    if sa.end_page < 1 || sa.end_page > INT_MAX {
        fmt.Fprintf(os.Stderr, "%s: invalid end page %d\n", progname, sa.end_page)
        usage()
        os.Exit(3)
	}
	if sa.page_len < 1 || sa.page_len > INT_MAX {
        fmt.Fprintf(os.Stderr, "%s: invalid page length %d\n", progname, sa.page_len)
        usage()
        os.Exit(4)
    }
    if sa.end_page < sa.start_page {
        fmt.Fprintf(os.Stderr, "%s: end page should not be less than start pag\n", progname)
        usage()
        os.Exit(3)
    }
    if sa.in_filename != "" {
        if _, err := os.Stat(sa.in_filename); os.IsNotExist(err) {
            fmt.Fprintf(os.Stderr, "%s: input file \"%s\" does not exist\n", progname, sa.in_filename)
			os.Exit(5);
        }
    }

}

func process_input(sa selpg_args) {
    var reader *bufio.Reader
    if sa.in_filename == "" {
        reader = bufio.NewReader(os.Stdin)
    } else {
        fin, err := os.Open(sa.in_filename)
        if err != nil {
            fmt.Fprintf(os.Stderr, "%s: could not open input file \"%s\"\n", progname, sa.in_filename)
            os.Exit(6)
        }
        reader = bufio.NewReader(fin)
        defer fin.Close()
    }

    var writer io.WriteCloser 
    if sa.print_dest == "" {
        writer = os.Stdout
    } else {
        cmd := exec.Command("lp", "-d" + sa.print_dest)
        var err error
        if writer, err = cmd.StdinPipe(); err != nil {
            fmt.Fprintf(os.Stderr, "%s: could not open pipe to \"%s\"\n", progname, sa.print_dest)
            fmt.Println(err)
            os.Exit(7)
        }
        cmd.Stdout = os.Stdout;
        cmd.Stderr = os.Stderr;
        if err = cmd.Start(); err != nil {
            fmt.Fprintf(os.Stderr, "%s: cmd start error\n", progname)
            fmt.Println(err)
            os.Exit(8)
        }
    }

    line_ctr, page_ctr, page_len := 1, 1, sa.page_len
    ptFlag := '\n'
    if sa.page_type {
        ptFlag = '\f'
        page_len = 1
    }

    // reader读数据并写入writer
    for {
        line, crc := reader.ReadString(byte(ptFlag));
        if crc != nil && len(line) == 0 {
            break
        }
        if line_ctr > page_len {
            page_ctr++
            line_ctr = 1
        }
        if page_ctr >= sa.start_page && page_ctr <= sa.end_page {
            _, err := writer.Write([]byte(line))
            if err != nil {
                fmt.Println(err)
                os.Exit(9)
            }
        }
        line_ctr++
    }

    // 判断读取是否成功或者是否完成
    if page_ctr < sa.start_page {
		fmt.Fprintf(os.Stderr, "\n%s: start_page (%d) greater than total pages (%d)," + " no output written\n", progname, sa.start_page, page_ctr)
	} else if page_ctr < sa.end_page {
		fmt.Fprintf(os.Stderr, "\n%s: end_page (%d) greater than total pages (%d)," + " less output than expected\n", progname, sa.end_page, page_ctr)
	}
}
